package com.a0soft.gphone.aTruffleHog.util;
/*
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 3, or (at your option) any
 * later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * Copyright(C) 2011 
 *   Sam Lu <ysl.sam.lu (at) gmail.com>
 *
 */

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileFilter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

import android.content.Context;
import android.text.TextUtils;

public class HttpCacheMgr {
    private File    m_fDir;
    private boolean m_bEnable;
    
    private static HttpCacheMgr m_inst;
    
    private static final long EXPIRED_TIME = CONSTS.HTTP_CACHE_EXPIRED_TIME_IN_MILLISECOND;
    
    private static final String DIR_NAME = "http";
    private static final String LAST_RUN_FILE = ".lastrun";
    private static final long CLEAR_CACHE_TIME = 30 * 86400L * 1000; //clear all expired cache file on every 30 days
    
    private static final String LOG_TAG = HttpCacheMgr.class.getSimpleName();

    //////////////////////////////////////////////////////////////
    public static HttpCacheMgr
    GetInstance(Context ctx)
    {
        if (m_inst == null)
            m_inst = new HttpCacheMgr(ctx);
        return m_inst;
    }
    
    private 
    HttpCacheMgr(Context ctx)
    {       
        try {
            File fDir = ctx.getCacheDir();
            
            if (fDir != null) {
                fDir = new File(fDir, DIR_NAME);
                if (!fDir.exists()) {
                    if (!fDir.mkdirs()) {
                        if (aDebug.ENABLED)
                            aDebug.d(LOG_TAG, "failed to mkdirs() for " + fDir.getAbsolutePath());
                        fDir = null;
                    }
                }
            }
            
            m_fDir = fDir;
            
            if (aDebug.ENABLED) {
                if (fDir != null)
                    aDebug.d(LOG_TAG, "m_fDir=" + m_fDir.getAbsolutePath());
                else
                    aDebug.d(LOG_TAG, "m_fDir is NULL");
            }
        }
        catch (Exception e) {
            m_fDir = null;
        }
        
        if (m_fDir == null)
            m_bEnable = false;
        else
            m_bEnable = true;
        
        if (m_bEnable) {
            _ClearOutOfDateCacheFiles();
        }
    }
    
    public final void
    SetEnable(boolean bEnable)
    {
        if (m_fDir == null) return;
        m_bEnable = bEnable;
    }
    
    public final String
    GetCacheData(String strUrlPath, boolean bCheckfExpired)
    {
        File f = _GetCacheFile(strUrlPath, false);
        if (f == null)
            return null;
        
        final long lExpiredTime = System.currentTimeMillis() - EXPIRED_TIME;        
        if (f.lastModified() < lExpiredTime) {
            if (bCheckfExpired) {
                //don't delete the file, let _ClearOutOfDateCacheFiles() do it
                //f.delete(); //expired
                return null;
            }
        }
        
        BufferedReader br = null;
        try {
            br = new BufferedReader(new FileReader(f), 8192);
            StringBuilder sb = new StringBuilder();
            char[] chBufs = new char[1024];
            int nLen;
            while ((nLen = br.read(chBufs)) >= 0) {
                sb.append(chBufs, 0, nLen);
            }
            br.close();
            br = null;
            return sb.toString();
        } catch (IOException e) {
            if (br != null) {
                try {
                    br.close();
                }
                catch (IOException e1) {}
            }
        }
        return null;
    }
    
    public final boolean
    SaveCacheData(String strUrlPath, String strData)
    {
        File f = _GetCacheFile(strUrlPath, true);
        if (f == null || strData == null) return false;

        BufferedWriter bw = null;
        try {
            bw = new BufferedWriter(new FileWriter(f), 8192);
            bw.write(strData);
            bw.close();
            bw = null;
        } catch (IOException e) {
            if (bw != null) {
                try {
                    bw.close();
                }
                catch (IOException e1) {}
            }
            return false;
        }
        
        if (aDebug.ENABLED)
            aDebug.d(LOG_TAG, "SaveCacheData(): save data to cache, " + f.getAbsolutePath());

        f.setLastModified(System.currentTimeMillis());
        return true;
    }
    
    private File
    _GetCacheFile(String strUrlPath, boolean bCreateIfNotExist)
    {
        if (!m_bEnable) return null;
        
        if (TextUtils.isEmpty(strUrlPath)) {
            aDebug.d(LOG_TAG, "_GetCacheFile(): cannot pass null object!");
            return null;
        }

        if (!m_fDir.exists()) {
            m_fDir.mkdirs();
        }
        
        String strFilename = MiscUtil.MD5(strUrlPath);
        
        File f = new File(m_fDir, strFilename);
        
        if (aDebug.ENABLED)
            aDebug.d(LOG_TAG, "_GetCacheFile(): " + strUrlPath + " => " + f.getAbsolutePath());

        if (f.exists()) {
            if (aDebug.ENABLED)
                aDebug.d(LOG_TAG, "  return cached file!");
            return f;
        }
        
        if (bCreateIfNotExist)
            return f;
        
        return null;
    }
    
    private void
    _ClearOutOfDateCacheFiles()
    {       
        final long lCurTime = System.currentTimeMillis();
        final long lExpiredTime = lCurTime - CLEAR_CACHE_TIME;
        long lLastCheckTime;

        File fLastRun = new File(m_fDir, LAST_RUN_FILE);
        if (!fLastRun.exists()) {
            try {
                fLastRun.createNewFile();
            } catch (IOException e) {
            }
            lLastCheckTime = lExpiredTime - 1;
        }
        else {
            lLastCheckTime = fLastRun.lastModified();
        }
        
        if (aDebug.ENABLED) {
            aDebug.d(LOG_TAG, "lCurTime=" + lCurTime);
            aDebug.d(LOG_TAG, "lExpiredTime=" + lExpiredTime);
            aDebug.d(LOG_TAG, "lLastCheckTime=" + lLastCheckTime);
        }
        
        if (lLastCheckTime < lExpiredTime) {
            File[] files = m_fDir.listFiles(new FileFilter() {
                @Override public boolean 
                accept(File f)
                {
                    if (f.lastModified() < lExpiredTime) {
                        if (f.getName().equals(LAST_RUN_FILE))
                            return false;
                        if (aDebug.ENABLED)
                            aDebug.d(LOG_TAG, "_ClearOutOfDateCacheFiles(): delete " + f.getName());
                        return true;
                    }
                    return false;
                }                   
            });
            
            if (files != null && files.length > 0) {
                if (aDebug.ENABLED)
                    aDebug.d(LOG_TAG, "_ClearOutOfDateCacheFiles(): delete caches, " + files.length);
                
                for (File fDel: files) {
                    fDel.delete();
                }
            }
            
            fLastRun.setLastModified(lCurTime);
        }
    }
}
